package jfprincesstests.structures.negotiation;

import java.util.*;

import jfprincesstests.fakes.*;
import jfuzzyprincepless.agents.BaseNegotiationAgent;
import jfuzzyprincepless.agents.INegotiatingAgent;
import jfuzzyprincepless.algorithm.structure.*;
import jfuzzyprincepless.zarchive.algorithm.structure.ChainCommand;
import jfuzzyprincepless.zarchive.algorithm.structure.ICommandConstruction;
import jfuzzyprincepless.zarchive.algorithm.structure.IMultiConstruction;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.*;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.fuzzyfor.INegotiationFuzzyFor;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.INegotiationFor;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.fuzzyfor.NegotiationFuzzyFor;
import jfuzzyprincepless.negotiation.DefaultNegotiation;
import jfuzzyprincepless.negotiation.DefaultNegotiationContext;
import jfuzzyprincepless.zarchive.negotiation.IAgentsExporter;
import jfuzzyprincepless.zarchive.negotiation.services.IExecuteForService;
import jfuzzyprincepless.negotiation.IExecutionContext;
import jfuzzyprincepless.negotiation.INegotiation;
import jfuzzyprincepless.negotiation.INegotiationContext;
import jfuzzyprincepless.negotiation.services.IAgentsListService;
import jfuzzyprincepless.zarchive.variables.FuzzySet;
import jfuzzyprincepless.zarchive.variables.IFuzzySet;
import jfuzzyprincepless.zarchive.negotiation.protocols.INegotiationProtocol__Q_del;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.DefaultExecForProtocol;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.ExecuteForNegotiation;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.DefaultNegotiation__Q;
import laboratoryQ.junit.ignore.patch.BaseTestCase;

public class TestForConstruction extends BaseTestCase {
	private static class TestBaseFor
		extends BaseNegotiationFor<Integer>
	{
		private Iterable<Integer> iterable;

		public TestBaseFor(
			Iterable<Integer> iterable,
			ICommandConstruction[] forBodyCommands,
			List<INegotiatingAgent> agents
		) {
			super(forBodyCommands, agents);
			this.iterable = iterable;
		}

		public TestBaseFor(
			Iterable<Integer> iterable,
			ICommandConstruction ... forBodyCommands
		) {
			this(
				iterable,
				forBodyCommands,
				new ArrayList<INegotiatingAgent>()
			);
		}

		public TestBaseFor(
		) {
			this(
				Collections.<Integer>emptyList(),
				ICommandConstruction.EMPTY_ARRAY,
				INegotiatingAgent.EMPTY_LIST
			);
		}

		@Override
		public Iterable<Integer> getIterable() {
			return iterable;
		}

		public void setItarable(Iterable<Integer> itarable) {
			this.iterable = itarable;
		}
	}

	public void testBaseForConstructionInit() {
		StringBuilder sb = new StringBuilder();

		TestBaseFor forInRange = new TestBaseFor();
		assertEquals(
			0,
			forInRange.getCommandsList().size()
		);
		forInRange.execute(new DefaultNegotiationContext());
		assertEquals("", sb.toString());

		forInRange.addBodyCommands(
			new FakeCmd(sb, "fakeCmd")
		);
		forInRange.execute(new DefaultNegotiationContext());
		assertEquals("", sb.toString());

		forInRange.setItarable(Arrays.asList(0, 1, 2, 3));
		forInRange.execute(new DefaultNegotiationContext());
		assertEquals(
			"fakeCmd;fakeCmd;fakeCmd;fakeCmd;",
			sb.toString()
			);
	}

	public void testExtractAgentsInBaseFor() {
		StringBuilder sb = new StringBuilder();
		FakeActivationCmd forBodyCommand =
			new FakeActivationCmd(new FakeForCmd<Integer>(sb, "cmd"));
		TestBaseFor forInRange = new TestBaseFor(
			Arrays.asList(0, 1, 2, 3, 4, 5),
			forBodyCommand,
			new FakeActivator(forBodyCommand)
		);

		assertEquals(0, forInRange.getAgents().size());

		forInRange.extractAgentsFromConstructions();
		assertEquals(1, forInRange.getAgents().size());

		ICommandConstruction forCommand = forInRange;
		forCommand.execute(new DefaultNegotiationContext());
		assertEquals("cmd0cmd1cmd2cmd3cmd4cmd5", sb.toString());
	}

	public void testChangeNegotiationProtocol() {
		IFuzzySet<Integer> forSet = new FuzzySet<Integer>();
		forSet.add(1, 0.1);
		NegotiationFuzzyFor<Integer> nffor =
			new NegotiationFuzzyFor<Integer>(forSet);
		nffor.execute(new DefaultNegotiationContext());

		nffor.setProtocol(
			new INegotiationProtocol__Q_del() {
				@Override
				public IExecuteForNegotiation<Integer> createNegotiation(
					ICommandConstruction construction,
					INegotiationContext context
				) {
					return null;
				}
			}
		);

		boolean caught = false;
		try {
			nffor.execute(IExecutionContext.EMPTY);
		} catch (NullPointerException e) {
			caught = true;
		}
		assertTrue(caught);
	}

	public void testDefaultExecForProtocol() {
		StringBuilder sb = new StringBuilder();

		List<Integer> intRangeList = Arrays.asList(0, 1, 2);
		FakeActivationCmd acmd =
			new FakeActivationCmd(new FakeForCmd<Integer>(sb, "A"));
		FakeActivationCmd bcmd =
			new FakeActivationCmd(new FakeForCmd<Integer>(sb, "B"));
		FakeActivationCmd ccmd =
			new FakeActivationCmd(new FakeForCmd<Integer>(sb, "C"));
		INegotiationFor<Integer> forInRange =
			new NegotiationForAbsEach<Integer>(
				intRangeList,
				new ICommandConstruction[] { acmd, bcmd, ccmd },
				Arrays.<INegotiatingAgent>asList(
					new FakeActivator(acmd),
					new FakeActivator(bcmd),
					new FakeActivator(ccmd)
				)
			);

		INegotiationProtocol__Q_del protocol =
			new DefaultExecForProtocol<Integer>();
		INegotiation negotiation =
			protocol.createNegotiation(forInRange, null);
		IExecutionContext context = negotiation.negotiate();
		negotiation.execute(context);
		assertEquals(
			"A0B0C0A1B1C1A2B2C2",
			sb.toString()
		);
	}

	public void testForNegotiation_Fails() {
		StringBuilder sb = new StringBuilder();

		FakeForCmd<Integer> forBodyCommand =
			new FakeForCmd<Integer>(sb, "cmd");
		NegotiationForAbsEach<Integer> forInRange =
			new NegotiationForAbsEach<Integer>(
				Arrays.asList(0, 1, 2, 3, 4, 5),
				forBodyCommand
			);

		ExecuteForNegotiation<Integer> negotiation =
			new ExecuteForNegotiation<Integer>(forInRange);

		boolean caught = false;
		try {
			negotiation.getCurrentElem();
			assertFalse(true);
		} catch (IllegalStateException e) {
			caught = true;
		}
		assertTrue(caught);

		ICommandConstruction forCommand = forInRange;
		forCommand.execute(new DefaultNegotiationContext());
		assertEquals("cmd0cmd1cmd2cmd3cmd4cmd5", sb.toString());

		caught = false;
		try {
			negotiation.getCurrentElem();
			assertFalse(true);
		} catch (IllegalStateException e) {
			caught = true;
		}
		assertTrue(caught);
	}

	public void testExecuteFuzzyForNegotiationProtocol() {
		StringBuilder sb = new StringBuilder();

		IFuzzySet<Integer> fuzzyIntSet = new FuzzySet<Integer>();
		fuzzyIntSet.add(1, 0.1);
		fuzzyIntSet.add(2, 0.5);
		fuzzyIntSet.add(3, 0.9);
		INegotiationFor<Integer> forLoop =
			new NegotiationFuzzyFor<Integer>(fuzzyIntSet);

		INegotiationProtocol__Q_del protocol =
			new DefaultExecForProtocol<Integer>();

		INegotiation negotiation =
			protocol.createNegotiation(forLoop, null);
		negotiation.negotiate();
		assertEquals("", sb.toString());

		FakeActivationCmd acmd =
			new FakeActivationCmd(new FakeFuzzyForCmd<Integer>(sb, ";A"));
		FakeActivationCmd bcmd =
			new FakeActivationCmd(new FakeFuzzyForCmd<Integer>(sb, ";B"));
		forLoop = new NegotiationFuzzyFor<Integer>(
			fuzzyIntSet,
			new ICommandConstruction[] { acmd, bcmd },
			Arrays.<INegotiatingAgent>asList(
				new FakeActivator(acmd),
				new FakeActivator(bcmd)
			)
		);
		negotiation = protocol.createNegotiation(forLoop, null);
		IExecutionContext context = negotiation.negotiate();
		negotiation.execute(context);
		assertEquals(
			";A1mem0.1mem:0.1;B1mem0.1mem:0.1" +
			";A2mem0.5mem:0.5;B2mem0.5mem:0.5" +
			";A3mem0.9mem:0.9;B3mem0.9mem:0.9",
			sb.toString()
		);
	}

	/**
	 * this test is example-pattern of for-agent-command usage:
	 * all commands are not active by the default
	 * for executes all commands
	 * activation agents activates only desired commands
	 */
	public void testDeactivationCommandsInFor() {
		StringBuilder sb = new StringBuilder();

		IFuzzySet<Integer> fuzzyIntSet = new FuzzySet<Integer>();
		fuzzyIntSet.add(1, 0.1);
		fuzzyIntSet.add(2, 0.5);
		fuzzyIntSet.add(3, 0.9);
		FakeActivationCmd cmd = new FakeActivationCmd(
			new FakeForCmd<Integer>(sb, "cmd")
		);
		NegotiationFuzzyFor<Integer> forLoop =
			new NegotiationFuzzyFor<Integer>(
				fuzzyIntSet,
				cmd
			);
		forLoop.extractAgentsFromConstructions();

		forLoop.execute(new DefaultNegotiationContext());
		assertEquals(
			"",
			sb.toString()
		);

		cmd.activate();
		forLoop.execute(new DefaultNegotiationContext());
		assertEquals(
			"cmd1mem0.1",
			sb.toString()
		);

		forLoop.getAgents().add(new FakeActivator(cmd));
		forLoop.execute(new DefaultNegotiationContext());
		assertEquals(
			"cmd1mem0.1cmd1mem0.1cmd2mem0.5cmd3mem0.9",
			sb.toString()
		);
	}

	public void testExtractAgentsInFuzzyFor() {
		StringBuilder sb = new StringBuilder();

		IFuzzySet<Integer> forSet = new FuzzySet<Integer>();
		forSet.add(1, 0.1);
		forSet.add(2, 0.5);
		forSet.add(3, 0.9);
		TestBaseFor nffor = new TestBaseFor(forSet.getElements());
		assertEquals(0, nffor.getAgents().size());

		FakeActivationCmd fakeCmd = new FakeActivationCmd(
			new FakeForCmd<Integer>(sb, "cmd")
		);
		nffor.addBodyCommands(new FakeActivator(fakeCmd), fakeCmd);
		assertEquals(0, nffor.getAgents().size());

		nffor.execute(new DefaultNegotiationContext());
		assertEquals("", sb.toString());

		nffor.extractAgentsFromConstructions();
		nffor.execute(new DefaultNegotiationContext());
		assertEquals(1, nffor.getAgents().size());
		assertEquals("cmd1cmd2cmd3", sb.toString());
	}

	public void testFuzzySetInFor() {
		IFuzzySet<Integer> fuzzyForSet = new FuzzySet<Integer>();
		fuzzyForSet.add(123, 0.45);
		INegotiationFuzzyFor<Integer> nff =
			new NegotiationFuzzyFor<Integer>(fuzzyForSet);
		assertEquals(
			0.45,
			nff.getFuzzyForSet().getMembershipDegree(123)
		);
		assertEquals(
			0.0,
			nff.getFuzzyForSet().getMembershipDegree(456)
		);

		assertSame(fuzzyForSet.getElements(), nff.getIterable());
	}

	public void testFroLoopExecutor() {
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
		final List<Integer> buffer = new ArrayList<Integer>();
		final ForLoopExecutor<Integer> forLoopExecutor =
			new ForLoopExecutor<Integer>(list);

		boolean caught = false;
		try {
			forLoopExecutor.getCurrentElem();
			assertFalse(true);
		} catch (IllegalStateException e) {
			caught = true;
		}
		assertTrue(caught);

		forLoopExecutor.execute(
			new Runnable() {
				@Override
				public void run() {
					Integer cur =
						forLoopExecutor.getCurrentElem();
					buffer.add(cur);
				}
			}
		);
		assertEquals(new ArrayList<Integer>(list), buffer);

		caught = false;
		try {
			forLoopExecutor.getCurrentElem();
			assertFalse(true);
		} catch (IllegalStateException e) {
			caught = true;
		}
		assertTrue(caught);
	}

	public void testDefaultForLoopRunnable() {
		final StringBuilder sb = new StringBuilder();
		final List<Integer> forSet = Arrays.asList(1, 2, 3);
		final ForLoopExecutor<Integer> executor =
			new ForLoopExecutor<Integer>(
				forSet
			);

		final List<INegotiatingAgent> agents =
			Arrays.<INegotiatingAgent>asList(
				new BaseNegotiationAgent() {
					@Override
					public boolean negotiate(
						INegotiationContext context
					) {
						sb.append("negotiate")
							.append(executor.getCurrentElem())
							.append(";");
						return false;
					}
				}
			);

		DefaultNegotiationContext context =
			new DefaultNegotiationContext();

		IMultiConstruction multiConstruction =
			new ChainCommand(
				new ICommandConstruction() {
					@Override
					public void execute(
						IExecutionContext context
					) {
						INegotiationContext ncontext =
							(INegotiationContext) context;
						ncontext.getService(IExecuteForService.class);
						sb.append("execute")
						.append(executor.getCurrentElem())
						.append(";");
					}
				}
			);

		IAgentsExporter agentsExporter =
			new IAgentsExporter() {
				@Override
				public List<INegotiatingAgent> getAgents() {
					return agents;
				}
			};
		Runnable runnable =
			new DefaultForLoopRunnable(
				new DefaultNegotiation__Q(
					multiConstruction,
					agentsExporter
				),
				context,
				context
			);
		executor.execute(runnable);
		assertEquals(
			"negotiate1;execute1;" +
			"negotiate2;execute2;" +
			"negotiate3;execute3;",
			sb.toString()
		);

		sb.setLength(0);
		runnable =
			new DefaultForLoopRunnable(
				new DefaultNegotiation__Q(
					multiConstruction,
					agentsExporter
				),
				(INegotiationContext)context
			);
		executor.execute(runnable);
		assertEquals(
			"negotiate1;" +
			"negotiate2;" +
			"negotiate3;",
			sb.toString()
		);

		sb.setLength(0);
		runnable =
			new DefaultForLoopRunnable(
				new DefaultNegotiation__Q(
					multiConstruction,
					agentsExporter
				),
				(IExecutionContext)context
			);
		executor.execute(runnable);
		assertEquals(
			"execute1;" +
			"execute2;" +
			"execute3;",
			sb.toString()
		);

		sb.setLength(0);
		DefaultForLoopRunnable dRunnable =
			new DefaultForLoopRunnable(
				new DefaultNegotiation__Q(
					multiConstruction,
					agentsExporter
				),
				(IExecutionContext)null
			);
		runnable = dRunnable;
		boolean caught = false;
		try {
			executor.execute(runnable);
		} catch (NullPointerException e) {
			caught = true;
		}
		assertTrue(caught);
		assertEquals("", sb.toString());
		dRunnable.setExecutionContext(context);
		dRunnable.setNegotiationContext(context);
		executor.execute(runnable);
		assertEquals(
			"execute1;" +
			"execute2;" +
			"execute3;",
			sb.toString()
		);
	}

	public void testSeparateForNegExec() {
		StringBuilder sb = new StringBuilder();

		NegotiationForAbsEach<Integer> forLoop =
			new NegotiationForAbsEach<Integer>(
				Arrays.asList(0, 1, 2),
				new FakeForCmd<Integer>(sb, ";cmd"),
				new FakeActivator(sb, ";neg")
			);
		forLoop.extractAgentsFromConstructions();
		ExecuteForNegotiation<Integer> efn =
			new ExecuteForNegotiation<Integer>(forLoop);
		IExecutionContext context = efn.negotiate();
		assertEquals("", sb.toString());
		efn.execute(context);
		assertEquals(
			";neg;cmd0;neg;cmd1;neg;cmd2",
			sb.toString()
		);

		sb.setLength(0);
		DefaultForLoopRunnable negotiationRunnable =
			new DefaultForLoopRunnable(
				new DefaultNegotiation__Q(forLoop, forLoop),
				(INegotiationContext)null
			);
		DefaultForLoopRunnable executionRunnable =
			new DefaultForLoopRunnable(
				new DefaultNegotiation__Q(forLoop, forLoop),
				(IExecutionContext)null
			);
		efn = new ExecuteForNegotiation<Integer>(
			forLoop,
			negotiationRunnable,
			executionRunnable
		);
		context = efn.negotiate();
		assertEquals(";neg;neg;neg", sb.toString());
		efn.execute(context);
		assertEquals(
			";neg;neg;neg;cmd0;cmd1;cmd2",
			sb.toString()
		);

		sb.setLength(0);
		efn = ExecuteForNegotiation.newSeparateFor(
			forLoop
		);
		context = efn.negotiate();
		assertEquals(";neg;neg;neg", sb.toString());
		efn.execute(context);
		assertEquals(
			";neg;neg;neg;cmd0;cmd1;cmd2",
			sb.toString()
		);
	}
	
	private static class TstContext2forAgents
		extends BaseNegotiationAgent
		implements IContext2forAgents, INegotiationConstruction
	{
		private StringBuilder sb;

		public TstContext2forAgents(StringBuilder sb) {
			this.sb = sb;
		}

		@Override
		public List<INegotiatingAgent> getAgents(
			INegotiationContext context
		) {
			return Arrays.<INegotiatingAgent>asList(
				this, this, this
			);
		}

		@Override
		public List<INegotiatingAgent> getAgentsList() {
			return Arrays.<INegotiatingAgent>asList(this);
		}

		@Override
		public void onNegotiationStart(INegotiationContext context) {
			sb.append("onNS: ").append(this);
		}

		@Override
		public String toString() {
			return "TstContext2forAgents";
		}
	}
	public void testElementsAsAgentsNegotiationFor() {
		StringBuilder sb = new StringBuilder();
		
		IContext2forAgents c2forAgents =
			new TstContext2forAgents(sb);
		jfuzzyprincepless.algorithm.structure.INegotiationFor eaFor =
			new ElementsAsAgentsNegotiationFor(c2forAgents);
		INegotiationContext context =
			new DefaultNegotiationContext();
		context.registerService(eaFor);
		assertEquals(
			"[ElementsAsAgentsNegotiationFor]",
			context.getService(IAgentsListService.class).getAgentsList().toString()
		);
		
		INegotiation negotiation = new DefaultNegotiation();
		negotiation.negotiate(context);
		assertEquals(
			"[TstContext2forAgents, TstContext2forAgents, TstContext2forAgents]",
			context.getService(IAgentsListService.class).getAgentsList().toString()
		);
		assertEquals(
			"onNS: TstContext2forAgentsonNS: TstContext2forAgentsonNS: TstContext2forAgents",
			sb.toString()
		);
	}
}
